/*
 * Copyright (c) 2010 Sylvestre Gallon <syl@pmbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/queue.h>
#include <objc.h>
#include <class.h>
#include <dispatch.h>
#include <stdio.h>
#include <string.h>

struct objc_dispatch_list *__objc_uninstall_dtable;

id
nil_method(id receiver, SEL op __attribute__((unused)))
{
        return receiver;
}

void
__objc_init_dispatch_tables(void)
{
        __objc_uninstall_dtable = 0;
        return ;
}

void
__objc_install_premature_dtable(Class cl)
{
        cl->dtable = __objc_uninstall_dtable;
        return ;
}

static
void
__objc_install_methods_in_dtable(Class cl, struct objc_method_list *mlist)
{
        struct objc_dispatch_elem *node;
        struct objc_method_list *ml;
        Method m;
        int i;

        ml = mlist;
        while (ml) {
                for (i = 0 ; i < ml->nb_methods ; i++) {
                       m = &ml->method_list[i];
                       node = __objc_malloc(sizeof(*node));

                       node->sel_id = &m->method_name->sel_id;
                       node->method_imp = m->method_imp;
                       TAILQ_INSERT_TAIL(&cl->dtable->list, node, entry);
                }
                ml = ml->next;
        }
}

void
__objc_install_dispatch_table_for_class(Class cl)
{
        Class super;

        if (cl->dtable != __objc_uninstall_dtable)
                return ;

        super = cl->super;

        if (super == 0) {
                cl->dtable = __objc_malloc(sizeof(struct objc_dispatch_list));
                TAILQ_INIT(&cl->dtable->list);
        } else {
                /* if dtable not installed for super we do it */
                super = objc_get_class((char *)super);
                if (super->dtable == __objc_uninstall_dtable) 
                        __objc_install_dispatch_table_for_class(super);
                cl->dtable = __objc_malloc(sizeof(struct objc_dispatch_list));
                TAILQ_INIT(&cl->dtable->list);
        }

        __objc_install_methods_in_dtable(cl, cl->methods);
        __objc_install_methods_in_dtable(cl, cl->meta->methods);
}

IMP
__objc_get_from_dispatch(Class receiver, SEL op)
{
        struct objc_dispatch_elem *node;

        if (receiver->dtable == __objc_uninstall_dtable) {
                return 0;
        }

        TAILQ_FOREACH(node, &receiver->dtable->list, entry) {
                if (strcmp(node->sel_id, op->sel_id) == 0) {
                        return node->method_imp;
                }
        }
	if (receiver->info == 1) 
		if (receiver->super != NULL)
        	        return (__objc_get_from_dispatch(
                                objc_get_class((char *)receiver->super), op));
	if (receiver->meta != NULL) 
		return (__objc_get_from_dispatch(receiver->meta, op));

        return (IMP)nil_method;
}
